//
//  Project.swift
//  JKTool
//
//  Created by 姜奎 on 2020/5/29.
//  Copyright © 2020 JK. All rights reserved.
//

import Foundation

public class Project {
    
    public let fileManager = FileManager.default
    
    public var directoryPath: String = ""
    
    public lazy var name: String = {
        return URL(fileURLWithPath: self.directoryPath).lastPathComponent
    }()
    public lazy var modulefilePath: String = {
        return self.directoryPath.appending("/Modulefile")
    }()
    
    public lazy var modulefile: String = {
        do {
            return try String(contentsOf: URL(fileURLWithPath: self.modulefilePath))
        } catch {
            print(Colors.green("【\(self.name)】:没有Modulefile文件"))
            return ""
        }
    }()
    public lazy var moduleList:[SubModule] = {
        var list:[SubModule] = []
        self.modulefile.enumerateLines { (line, stop) in
            let scannerWithComments = Scanner(string: line)

            if scannerWithComments.scanString("#") != nil {
                return
            }
            if scannerWithComments.isAtEnd {
                // The line was all whitespace.
                return
            }
            
            guard let remainingString = scannerWithComments.remainingSubstring.map(String.init)?.replacingOccurrences(of: "\"", with: "") else {
                stop = true
                return
            }
            
            var arr = remainingString.components(separatedBy: " ").filter { (str) -> Bool in
                return str != ""
            }
            if arr.count <= 2 {
                return
            }
            if arr.contains("==") {
                
            } else if arr.contains(">=") {
                
            } else if arr.contains("~>") {
                
            }
            
            let module = SubModule(source: arr[0], url: arr[1], branch: (arr.count >= 3) ? (arr[2]) : "" )
            list.append(module)
        }
        return list
    }()
    
    public lazy var recordListPath: String = {
        return self.directoryPath.appending("/Modulefile.recordList")
    }()
    
    public lazy var recordList: [String] = {
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: self.modulefilePath))
            let recordList = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! Array<String>
            return recordList
        } catch {
            print(Colors.yellow("【\(self.name)】Modulefile.recordList 读取失败"))
            return []
        }
    }()
    
    public lazy var checkoutsPath: String = {
        let modulePath = self.modulePath.appending("/checkouts")
        return modulePath
    }()
    
    public lazy var buildsPath: String = {
        let buildPath = self.modulePath.appending("/Builds")
        return buildPath
    }()
    
    public lazy var buildPath: String = {
        let buildPath = self.directoryPath.appending("/Build")
        return buildPath
    }()
    
    public lazy var modulePath: String = {
        let buildPath = self.directoryPath.appending("/Module")
        return buildPath
    }()
    
    public lazy var isWorkspaceRootPro: Bool = {
        let isExist = self.fileManager.fileExists(atPath: self.rootPath+"/"+self.name+".xcworkspace", isDirectory: nil)
        return isExist
    }()
    
    public lazy var rootCheckoutsPath: String = {
        let modulePath = self.rootModulePath.appending("/checkouts")
        return modulePath
    }()
    
    public lazy var rootBuildPath: String = {
        let buildPath = self.rootPath.appending("/Build")
        return buildPath
    }()
    
    public lazy var rootBuildsPath: String = {
        let buildPath = self.rootModulePath.appending("/Builds")
        return buildPath
    }()
    
    public lazy var rootModulePath: String = {
        let buildPath = self.rootPath.appending("/Module")
        return buildPath
    }()
    
    lazy var rootPath: String = {
        let isExist = self.directoryPath.contains("/Module/checkouts")
        var rootPath = self.directoryPath
        if isExist {
            rootPath = self.directoryPath.replacingOccurrences(of: "/Module/checkouts/"+self.name, with: "")
        }
        return rootPath
    }()

    init(directoryPath: String = FileManager.default.currentDirectoryPath) {
        self.directoryPath = directoryPath
    }

}

extension String {

    /// Returns self without any potential trailing Cartfile comment. A Cartfile
    /// comment starts with the first `commentIndicator` that is not embedded in any quote
    var strippingTrailingCartfileComment: String {

        // Since the Cartfile syntax doesn't support nested quotes, such as `"version-\"alpha\""`,
        // simply consider any odd-number occurence of a quote as a quote-start, and any
        // even-numbered occurrence of a quote as quote-end.
        // The comment indicator (e.g. `#`) is the start of a comment if it's not nested in quotes.
        // The following code works also for comment indicators that are are more than one character
        // long (e.g. double slashes).

        let quote = "\""

        // Splitting the string by quote will make odd-numbered chunks outside of quotes, and
        // even-numbered chunks inside of quotes.
        // `omittingEmptySubsequences` is needed to maintain this property even in case of empty quotes.
        let quoteDelimitedChunks = self.split(
            separator: quote.first!,
            maxSplits: Int.max,
            omittingEmptySubsequences: false
        )

        for (offset, chunk) in quoteDelimitedChunks.enumerated() {
            let isInQuote = offset % 2 == 1 // even chunks are not in quotes, see comment above
            if isInQuote {
                continue // don't consider comment indicators inside quotes
            }
            if let range = chunk.range(of: "#") {
                // there is a comment, return everything before its position
                let advancedOffset = (..<offset).relative(to: quoteDelimitedChunks)
                let previousChunks = quoteDelimitedChunks[advancedOffset]
                let chunkBeforeComment = chunk[..<range.lowerBound]
                return (previousChunks + [chunkBeforeComment])
                    .joined(separator: quote) // readd the quotes that were removed in the initial split
            }
        }
        return self
    }
}

extension Scanner {

    /// Scans a string that is supposed to start with the given prefix, until the given
    /// string is encountered.
    /// - returns: the scanned string without the prefix. If the string does not start with the prefix,
    /// or the scanner is at the end, it returns `nil` without advancing the scanner.
    fileprivate func scanStringWithPrefix(_ prefix: Character, until: String) -> String? {
        guard !self.isAtEnd, self.remainingSubstring?.first == prefix else { return nil }

        var buffer: NSString?
        self.scanUpTo(until, into: &buffer)
        guard let stringWithPrefix = buffer as String?, stringWithPrefix.first == prefix else {
            return nil
        }

        return String(stringWithPrefix.dropFirst())
    }

    /// The string (as `Substring?`) that is left to scan.
    ///
    /// Accessing this variable will not advance the scanner location.
    ///
    /// - returns: `nil` in the unlikely event `self.scanLocation` splits an extended grapheme cluster.
    var remainingSubstring: Substring? {
        return Range(
            NSRange(
                location: self.scanLocation /* our UTF-16 offset */,
                length: (self.string as NSString).length - self.scanLocation
            ),
            in: self.string
        ).map {
            self.string[$0]
        }
    }
}
